<?php
/**
 * Created by linzhonghuang.
 * Github: git@github.com:lichtung/Sharina.git
 * Email: 784855684@qq.com
 * Date: 2016/12/11
 * Time: 10:48
 */

use application\module\explorer\libs\Member;
use application\module\explorer\libs\FileCache;
use wshore\core\Request;
use wshore\core\Lang;
use application\module\explorer\libs\Util;
use application\module\explorer\libs\Mime;
const KOD_VERSION = '5.23';
//----------------------------------------- CONFIG ----------------------------------------------------------------------
/**
 * @param $path
 * @return mixed
 */
function P($path)
{
    return str_replace('\\', '/', $path);
}

const ENTRY_FILE = 'explorer.php';
const SR_PATH_EXPLORER = __DIR__ . '/';//dirname(dirname(__FILE__))

$web_root = str_replace(P($_SERVER['SCRIPT_NAME']), '', SR_PATH_EXPLORER . ENTRY_FILE) . '/';
if (substr($web_root, -10) == 'explorer.php/') {//解决部分主机不兼容问题
    $web_root = P($_SERVER['DOCUMENT_ROOT']) . '/';
}

define('WEB_ROOT', $web_root);
define('HOST', (WS_IS_HTTPS ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . '/');

define('APPHOST', HOST /*. str_replace(WEB_ROOT, '', WS_PATH_BASE)*/);//程序根目录
//dumpout(HOST, WEB_ROOT, APPHOST);

define('TEMPLATE', WS_PATH_APP . 'module/explorer/view/');    //模版文件路径
define('CONTROLLER_DIR', SR_PATH_EXPLORER . 'controller/'); //控制器目录
define('LIB_DIR', SR_PATH_EXPLORER . 'lib/');        //库目录
define('CLASS_DIR', LIB_DIR . 'class/');            //内目录
define('CORER_DIR', LIB_DIR . 'core/');            //核心目录
define('LANGUAGE_PATH', SR_PATH_EXPLORER . 'i18n/');        //多语言目录

define('DATA_PATH', WS_PATH_DATA . 'explorer/');       //用户数据目录
Util::checkDir(DATA_PATH);
define('LOG_PATH', WS_PATH_RUNTIME . 'log/');         //日志目录
Util::checkDir(LOG_PATH);
define('USER_SYSTEM', DATA_PATH . 'system/');      //用户数据存储目录
Util::checkDir(USER_SYSTEM);
define('DATA_THUMB', DATA_PATH . 'thumb/');       //缩略图生成存放
Util::checkDir(DATA_THUMB);

define('STATIC_JS', 'app');  //_dev(开发状态)||app(打包压缩)
define('STATIC_LESS', 'css');//less(开发状态)||css(打包压缩)
define('STATIC_PATH', './assets/');//静态文件目录
//define('STATIC_PATH','http://static.kalcaddle.com/static/');//静态文件统分离,可单独将static部署到CDN

/*
 可以自定义【用户目录】和【公共目录】;移到web目录之外，
 可以使程序更安全, 就不用限制用户的扩展名权限了;
 */
define('USER_PATH', DATA_PATH . 'User/');        //用户目录
Util::checkDir(USER_PATH);
//自定义用户目录；需要先将data/User移到别的地方 再修改配置，例如：
//define('USER_PATH',   DATA_PATH .'/Library/WebServer/Documents/User');
define('PUBLIC_PATH', DATA_PATH . 'public/');     //公共目录
Util::checkDir(PUBLIC_PATH);
//公共共享目录,读写权限跟随用户目录的读写权限 再修改配置，例如：
//define('PUBLIC_PATH','/Library/WebServer/Documents/Public/');

/*
 * office服务器配置；默认调用的微软的接口，程序需要部署到外网。
 * 本地部署weboffice 引号内填写office解析服务器地址 形如:  http://---/view.aspx?src=
 */
const OFFICE_SERVER = 'https://view.officeapps.live.com/op/view.aspx?src=';


/**
 * 系统函数：                filesize(),file_exists(),pathinfo(),rname(),unlink(),filemtime(),is_readable(),is_wrieteable();
 * 获取文件详细信息        file_info($file_name)
 * 获取文件夹详细信息        path_info($dir)
 * 递归获取文件夹信息        path_info_more($dir,&$file_num=0,&$path_num=0,&$size=0)
 * 获取文件夹下文件列表    path_list($dir)
 * 路径当前文件[夹]名        get_path_this($path)
 * 获取路径父目录            get_path_father($path)
 * 删除文件                del_file($file)
 * 递归删除文件夹            del_dir($dir)
 * 递归复制文件夹            copy_dir($source, $dest)
 * 创建目录                mk_dir($dir, $mode = 0777)
 * 文件大小格式化            size_format($bytes, $precision = 2)
 * 判断是否绝对路径        path_is_absolute( $path )
 * 扩展名的文件类型        ext_type($ext)
 * 文件下载                file_download($file)
 * 文件下载到服务器        file_download_this($from, $file_name)
 * 获取文件(夹)权限        get_mode($file)  //rwx_rwx_rwx [文件名需要系统编码]
 * 上传文件(单个，多个)    upload($fileInput, $path = './');//
 * 获取配置文件项            get_config($file, $ini, $type="string")
 * 修改配置文件项            update_config($file, $ini, $value,$type="string")
 * 写日志到LOG_PATH下        write_log('dd','default|.自建目录.','log|error|warning|debug|info|db')
 */

/**
 * 传入参数为程序编码时，有传出，则用程序编码，
 * 传入参数没有和输出无关时，则传入时处理成系统编码。
 * @param $str
 * @return string
 */
function iconv_app($str)
{
    global $config;
    $result = iconv($config['system_charset'], $config['app_charset'], $str);
    if (strlen($result) == 0) {
        $result = $str;
    }
    return $result;
}

function iconv_system($str)
{
    global $config;
    $result = iconv($config['app_charset'], $config['system_charset'], $str);
    if (strlen($result) == 0) {
        $result = $str;
    }
    return $result;
}

function get_filesize($path)
{
    @$ret = abs(sprintf("%u", filesize($path)));
    return (int)$ret;
}

/**
 * 获取文件详细信息
 * 文件名从程序编码转换成系统编码,传入utf8，系统函数需要为gbk
 * @param string $path
 * @return array
 */
function file_info($path)
{
    $name = get_path_this($path);
    $size = get_filesize($path);
    $info = array(
        'name' => iconv_app($name),
        'path' => iconv_app(get_path_father($path)),
        'ext' => get_path_ext($path),
        'type' => 'file',
        'mode' => get_mode($path),
        'atime' => fileatime($path), //最后访问时间
        'ctime' => filectime($path), //创建时间
        'mtime' => filemtime($path), //最后修改时间
        'is_readable' => intval(is_readable($path)),
        'is_writeable' => intval(is_writeable($path)),
        'size' => $size,
        'size_friendly' => size_format($size, 2)
    );
    return $info;
}

/**
 * 获取文件夹细信息
 * @param $path
 * @return array
 */
function folder_info($path)
{
    $info = array(
        'name' => iconv_app(get_path_this($path)),
        'path' => iconv_app(get_path_father($path)),
        'type' => 'folder',
        'mode' => get_mode($path),
        'atime' => fileatime($path), //访问时间
        'ctime' => filectime($path), //创建时间
        'mtime' => filemtime($path), //最后修改时间
        'is_readable' => intval(is_readable($path)),
        'is_writeable' => intval(is_writeable($path))
    );
    return $info;
}


/**
 * 获取一个路径(文件夹&文件) 当前文件[夹]名
 * test/11/ ==>11 test/1.c  ==>1.c
 * @param $path
 * @return bool|string
 */
function get_path_this($path)
{
    $path = str_replace('\\', '/', rtrim(trim($path), '/'));
    return substr($path, strrpos($path, '/') + 1);
}

/**
 * 获取一个路径(文件夹&文件) 父目录
 * /test/11/==>/test/   /test/1.c ==>/www/test/
 * @param $path
 * @return bool|string
 */
function get_path_father($path)
{
    $path = str_replace('\\', '/', rtrim(trim($path), '/'));
    return substr($path, 0, strrpos($path, '/') + 1);
}

/**
 * 获取扩展名
 * @param $path
 * @return bool|string
 */
function get_path_ext($path)
{
    $name = get_path_this($path);
    $ext = '';
    if (strstr($name, '.')) {
        $ext = substr($name, strrpos($name, '.') + 1);
        $ext = strtolower($ext);
    }
    if (strlen($ext) > 3 && preg_match("/([\x81-\xfe][\x40-\xfe])/", $ext, $match)) {
        $ext = '';
    }
    return $ext;
}

//自动获取不重复文件(夹)名
//如果传入$file_add 则检测存在则自定重命名  a.txt 为a{$file_add}.txt
//$same_file_type  rename,replace,skip,folder_rename
function get_filename_auto($path, $file_add = "", $same_file_type = 'replace')
{
    if (is_dir($path) && $same_file_type != 'folder_rename') {//文件夹则忽略
        return $path;
    }
    //重名处理
    if (file_exists($path)) {
        if ($same_file_type == 'replace') {
            return $path;
        } else if ($same_file_type == 'skip') {
            return false;
        }
    }

    $i = 1;
    $father = get_path_father($path);
    $name = get_path_this($path);
    $ext = get_path_ext($name);
    if (strlen($ext) > 0) {
        $ext = '.' . $ext;
        $name = substr($name, 0, strlen($name) - strlen($ext));
    }
    while (file_exists($path)) {
        if ($file_add != '') {
            $path = $father . $name . $file_add . $ext;
            $file_add .= '-';
        } else {
            $path = $father . $name . '(' . $i . ')' . $ext;
            $i++;
        }
    }
    return $path;
}

/**
 * 文件或目录是否可写 is_writeable();
 * 兼容性处理：挂载目录755 bug
 * @param $path
 * @return bool|string
 */
function path_writable($path)
{
    if (is_dir($path)) {
        $file = $path . '/writeable_test_' . time() . '.txt';
        touch($file);
        if (file_exists($file)) {
            @unlink($file);
            return true;
        }
    } else if (is_file($path)) {
        $fp = @fopen($path, 'a+');
        if ($fp) {
            fclose($fp);
            return true;
        }
        fclose($fp);
    }
    return false;
}

/**
 * 获取文件夹详细信息,文件夹属性时调用，包含子文件夹数量，文件数量，总大小
 * @param $path
 * @return array
 */
function path_info($path)
{
    //if (!is_dir($path)) return false;
    $pathinfo = _path_info_more($path);//子目录文件大小统计信息
    $folderinfo = folder_info($path);
    return array_merge($pathinfo, $folderinfo);
}

/**
 * 检查名称是否合法
 * @param $path
 * @return bool|string
 */
function path_check($path)
{
    $check = array('/', '\\', ':', '*', '?', '"', '<', '>', '|');
    $path = rtrim($path, '/');
    $path = get_path_this($path);
    foreach ($check as $v) {
        if (strstr($path, $v)) {
            return false;
        }
    }
    return true;
}

/**
 * 递归获取文件夹信息： 子文件夹数量，文件数量，总大小
 * @param $dir
 * @param int $file_num
 * @param int $path_num
 * @param int $size
 * @return array
 */
function _path_info_more($dir, &$file_num = 0, &$path_num = 0, &$size = 0)
{
    if (!$dh = opendir($dir)) return [];
    while (($file = readdir($dh)) !== false) {
        if ($file != "." && $file != "..") {
            $fullpath = $dir . "/" . $file;
            if (!is_dir($fullpath)) {
                $file_num++;
                $size += get_filesize($fullpath);
            } else {
                _path_info_more($fullpath, $file_num, $path_num, $size);
                $path_num++;
            }
        }
    }
    closedir($dh);
    $pathinfo['file_num'] = $file_num;
    $pathinfo['folder_num'] = $path_num;
    $pathinfo['size'] = $size;
    $pathinfo['size_friendly'] = size_format($size);
    return $pathinfo;
}


/**
 * 获取多选文件信息,包含子文件夹数量，文件数量，总大小，父目录权限
 * @param $list
 * @ param $time_type
 * @return array
 */
function path_info_muti($list/*, $time_type*/)
{
    if (count($list) == 1) {
        if ($list[0]['type'] == "folder") {
            return path_info($list[0]['path']/*, $time_type*/);
        } else {
            return file_info($list[0]['path']/*, $time_type*/);
        }
    }
    $pathinfo = array(
        'file_num' => 0,
        'folder_num' => 0,
        'size' => 0,
        'size_friendly' => '',
        'father_name' => '',
        'mod' => ''
    );
    foreach ($list as $val) {
        if ($val['type'] == 'folder') {
            $pathinfo['folder_num']++;
            $temp = path_info($val['path']);
            $pathinfo['folder_num'] += $temp['folder_num'];
            $pathinfo['file_num'] += $temp['file_num'];
            $pathinfo['size'] += $temp['size'];
        } else {
            $pathinfo['file_num']++;
            $pathinfo['size'] += get_filesize($val['path']);
        }
    }
    $pathinfo['size_friendly'] = size_format($pathinfo['size']);
    $father_name = get_path_father($list[0]['path']);
    $pathinfo['mode'] = get_mode($father_name);
    return $pathinfo;
}

/**
 * 获取文件夹下列表信息
 * @param string $dir 包含结尾/   d:/wwwroot/test/ 传入需要读取的文件夹路径,为程序编码
 * @param bool $list_file
 * @param bool $check_children
 * @return array
 */
function path_list($dir, $list_file = true, $check_children = false)
{
    $dir = rtrim($dir, '/') . '/';
    if (!is_dir($dir) || !($dh = opendir($dir))) {
        return array('folderlist' => array(), 'filelist' => array());
    }
    $folderlist = array();
    $filelist = array();//文件夹与文件
    while (($file = readdir($dh)) !== false) {
        if ($file != "." && $file != ".." && $file != ".svn") {
            $fullpath = $dir . $file;
            if (is_dir($fullpath)) {
                $info = folder_info($fullpath);
                if ($check_children) {
                    $info['isParent'] = path_haschildren($fullpath, $list_file);
                }
                $folderlist[] = $info;
            } else if ($list_file) {//是否列出文件
                $info = file_info($fullpath);
                if ($check_children) $info['isParent'] = false;
                $filelist[] = $info;
            }
        }
    }
    closedir($dh);
    return array('folderlist' => $folderlist, 'filelist' => $filelist);
}

// 判断文件夹是否含有子内容【区分为文件或者只筛选文件夹才算】
function path_haschildren($dir, $check_file = false)
{
    $dir = rtrim($dir, '/') . '/';
    if (!$dh = @opendir($dir)) return false;
    while (($file = readdir($dh)) !== false) {
        if ($file != "." && $file != "..") {
            $fullpath = $dir . $file;
            if ($check_file) {//有子目录或者文件都说明有子内容
                if (is_file($fullpath) || is_dir($fullpath . '/')) {
                    return true;
                }
            } else {//只检查有没有文件
                @$ret = (is_dir($fullpath . '/'));
                return (bool)$ret;
            }
        }
    }
    closedir($dh);
    return false;
}

/**
 * 删除文件 传入参数编码为操作系统编码. win--gbk
 * @param $fullpath
 * @return bool
 */
function del_file($fullpath)
{
    if (!@unlink($fullpath)) { // 删除不了，尝试修改文件权限
        @chmod($fullpath, 0777);
        if (!@unlink($fullpath)) {
            return false;
        }
    }
    return true;
}

/**
 * 删除文件夹 传入参数编码为操作系统编码. win--gbk
 * @param $dir
 * @return bool
 */
function del_dir($dir)
{
    if (!$dh = opendir($dir)) return false;
    while (($file = readdir($dh)) !== false) {
        if ($file != "." && $file != "..") {
            $fullpath = $dir . '/' . $file;
            if (!is_dir($fullpath)) {
                if (!unlink($fullpath)) { // 删除不了，尝试修改文件权限
                    chmod($fullpath, 0777);
                    if (!unlink($fullpath)) {
                        return false;
                    }
                }
            } else {
                if (!del_dir($fullpath)) {
                    chmod($fullpath, 0777);
                    if (!del_dir($fullpath)) return false;
                }
            }
        }
    }
    closedir($dh);
    if (rmdir($dir)) {
        return true;
    } else {
        return false;
    }
}

/**
 * 复制文件夹
 * eg:将D:/wwwroot/下面wordpress复制到
 *    D:/wwwroot/www/explorer/0000/del/1/
 * 末尾都不需要加斜杠，复制到地址如果不加源文件夹名，
 * 就会将wordpress下面文件复制到D:/wwwroot/www/explorer/0000/del/1/下面
 * $from = 'D:/wwwroot/wordpress';
 * $to = 'D:/wwwroot/www/explorer/0000/del/1/wordpress';
 * @param $source
 * @param $dest
 * @return bool
 */
function copy_dir($source, $dest)
{
    if (!$dest) return false;

    if ($source == substr($dest, 0, strlen($source))) return false;//防止父文件夹拷贝到子文件夹，无限递归
    $result = false;
    if (is_file($source)) {
        if ($dest[strlen($dest) - 1] == '/') {
            $__dest = $dest . "/" . basename($source);
        } else {
            $__dest = $dest;
        }
        $result = copy($source, $__dest);
        chmod($__dest, 0777);
    } elseif (is_dir($source)) {
        if ($dest[strlen($dest) - 1] == '/') {
            $dest = $dest . basename($source);
        }
        if (!is_dir($dest)) {
            mkdir($dest, 0777, true);
        }
        if (!$dh = opendir($source)) return false;
        while (($file = readdir($dh)) !== false) {
            if ($file != "." && $file != "..") {
                if (!is_dir($source . "/" . $file)) {
                    $__dest = $dest . "/" . $file;
                } else {
                    $__dest = $dest . "/" . $file;
                }
                $result = copy_dir($source . "/" . $file, $__dest);
            }
        }
        closedir($dh);
    }
    return $result;
}

/**
 * 创建目录
 *
 * @param string $dir
 * @param int $mode
 * @return bool
 */
function mk_dir($dir, $mode = 0777)
{
    if (is_dir($dir) or mkdir($dir, $mode, true)) {
        return true;
    }
    if (!mk_dir(dirname($dir), $mode)) {
        return false;
    }
    return mkdir($dir, $mode, true);
}

/*
* 获取文件&文件夹列表(支持文件夹层级)
* path : 文件夹 $dir ——返回的文件夹array files ——返回的文件array
* $deepest 是否完整递归；$deep 递归层级
*/
function recursion_dir($path, &$dir, &$file, $deepest = -1, $deep = 0)
{
    $path = rtrim($path, '/') . '/';
    if (!is_array($file)) $file = array();
    if (!is_array($dir)) $dir = array();
    if (!$dh = opendir($path)) return false;
    while (($val = readdir($dh)) !== false) {
        if ($val == '.' || $val == '..') continue;
        $value = strval($path . $val);
        if (is_file($value)) {
            $file[] = $value;
        } else if (is_dir($value)) {
            $dir[] = $value;
            if ($deepest == -1 || $deep < $deepest) {
                recursion_dir($value . "/", $dir, $file, $deepest, $deep + 1);
            }
        }
    }
    closedir($dh);
    return true;
}

/*
 * $search 为包含的字符串
 * is_content 表示是否搜索文件内容;默认不搜索
 * is_case  表示区分大小写,默认不区分
 */
function path_search($path, $search, $is_content = false, $file_ext = '', $is_case = false)
{
    $ext_arr = explode("|", $file_ext);
    recursion_dir($path, $dirs, $files, -1, 0);
    $strpos = 'stripos';//是否区分大小写
    if ($is_case) $strpos = 'strpos';

    $filelist = array();
    $folderlist = array();
    foreach ($files as $f) {
        $ext = get_path_ext($f);
        $path_this = get_path_this($f);
        if ($file_ext != '' && !in_array($ext, $ext_arr)) continue;//文件类型不在用户限定内
        if ($strpos($path_this, $search) !== false) {//搜索文件名;搜到就返回；搜不到继续
            $filelist[] = file_info($f);
            continue;
        }
        if ($is_content && is_file($f)) {
            $fp = fopen($f, "r");
            $content = @fread($fp, get_filesize($f));
            fclose($fp);
            if ($strpos($content, iconv_app($search)) !== false) {
                $filelist[] = file_info($f);
            }
        }
    }
    if ($file_ext == '') {//没限定扩展名则才搜索文件夹
        foreach ($dirs as $f) {
            $path_this = get_path_this($f);
            if ($strpos($path_this, $search) !== false) {
                $folderlist[] = array(
                    'name' => iconv_app(get_path_this($f)),
                    'path' => iconv_app(get_path_father($f))
                );
            }
        }
    }
    return array('folderlist' => $folderlist, 'filelist' => $filelist);
}

/**
 * 修改文件、文件夹权限
 * @param string $path 文件(夹)目录
 * @param int $mod
 * @return bool
 */
function chmod_path($path, $mod)
{
    //$mod = 0777;//
    if (!isset($mod)) $mod = 0777;
    if (!is_dir($path)) return @chmod($path, $mod);
    if (!$dh = opendir($path)) return false;
    while (($file = readdir($dh)) !== false) {
        if ($file != "." && $file != "..") {
            $fullpath = $path . '/' . $file;
            chmod($fullpath, $mod);
            return chmod_path($fullpath, $mod);
        }
    }
    closedir($dh);
    return chmod($path, $mod);
}

/**
 * 文件大小格式化
 *
 * @param int $bytes 文件大小
 * @param int $precision 保留小数点
 * @return string
 */
function size_format($bytes, $precision = 2)
{
    if ($bytes == 0) return "0 B";
    $unit = array(
        'TB' => 1099511627776,  // pow( 1024, 4)
        'GB' => 1073741824,        // pow( 1024, 3)
        'MB' => 1048576,        // pow( 1024, 2)
        'kB' => 1024,            // pow( 1024, 1)
        'B ' => 1,                // pow( 1024, 0)
    );
    foreach ($unit as $un => $mag) {
        if (doubleval($bytes) >= $mag)
            return round($bytes / $mag, $precision) . ' ' . $un;
    }
    return '';
}

/**
 * 判断路径是不是绝对路径
 * 返回true('/foo/bar','c:\windows').
 * @param $path
 * @return bool 返回true则为绝对路径，否则为相对路径
 */
function path_is_absolute($path)
{
    if (realpath($path) == $path)// *nux 的绝对路径 /home/my
        return true;
    if (strlen($path) == 0 || $path[0] == '.')
        return false;
    if (preg_match('#^[a-zA-Z]:\\\\#', $path))// windows 的绝对路径 c:\aaa\
        return true;
    return (bool)preg_match('#^[/\\\\]#', $path); //绝对路径 运行 / 和 \绝对路径，其他的则为相对路径
}

/**
 * 获取扩展名的文件类型
 *
 * @param string $ext 扩展名
 * @return int|string
 */
function ext_type($ext)
{
    $ext2type = array(
        'text' => array('txt', 'ini', 'log', 'asc', 'csv', 'tsv', 'vbs', 'bat', 'cmd', 'inc', 'conf', 'inf'),
        'code' => array('css', 'htm', 'html', 'php', 'js', 'c', 'cpp', 'h', 'java', 'cs', 'sql', 'xml'),
        'picture' => array('jpg', 'jpeg', 'png', 'gif', 'ico', 'bmp', 'tif', 'tiff', 'dib', 'rle'),
        'audio' => array('mp3', 'ogg', 'oga', 'mid', 'midi', 'ram', 'wav', 'wma', 'aac', 'ac3', 'aif', 'aiff', 'm3a', 'm4a', 'm4b', 'mka', 'mp1', 'mx3', 'mp2'),
        'flash' => array('swf'),
        'video' => array('rm', 'rmvb', 'flv', 'mkv', 'wmv', 'asf', 'avi', 'aiff', 'mp4', 'divx', 'dv', 'm4v', 'mov', 'mpeg', 'vob', 'mpg', 'mpv', 'ogm', 'ogv', 'qt'),
        'document' => array('doc', 'docx', 'docm', 'dotm', 'odt', 'pages', 'pdf', 'rtf', 'xls', 'xlsx', 'xlsb', 'xlsm', 'ppt', 'pptx', 'pptm', 'odp'),
        'rar_achieve' => array('rar', 'arj', 'tar', 'ace', 'gz', 'lzh', 'uue', 'bz2'),
        'zip_achieve' => array('zip', 'gzip', 'cab', 'tbz', 'tbz2'),
        'other_achieve' => array('dmg', 'sea', 'sit', 'sqx')
    );
    foreach ($ext2type as $type => $exts) {
        if (in_array($ext, $exts)) {
            return $type;
        }
    }
    return null;
}

/**
 * 输出、文件下载
 * 默认以附件方式下载；$download为false时则为输出文件
 * @param $file
 * @param bool $download
 */
function file_put_out($file, $download = false)
{
    if (!is_file($file)) show_json('not a file!');
    if (!file_exists($file)) show_json('file not exists', false);
    if (!is_readable($file)) show_json('file not readable', false);

    set_time_limit(0);
    ob_clean();//清除之前所有输出缓冲
    $mime = Mime::getByExt(get_path_ext($file));
    if ($download ||
        (strstr($mime, 'application/') && $mime != 'application/x-shockwave-flash')
    ) {//下载或者application则设置下载头
        $filename = get_path_this($file);//解决在IE中下载时中文乱码问题
        if (preg_match('/MSIE/', $_SERVER['HTTP_USER_AGENT']) ||
            preg_match('/Trident/', $_SERVER['HTTP_USER_AGENT'])
        ) {
            if ($GLOBALS['config']['system_os'] != 'windows') {//win主机 ie浏览器；中文文件下载urlencode问题
                $filename = str_replace('+', '%20', urlencode($filename));
            }
        }
        header("Content-Type: application/octet-stream");
        header("Content-Disposition: attachment;filename=" . $filename);
    } else {
        //缓存文件
        header('Expires: ' . date('D, d M Y H:i:s', time() + 3600 * 24 * 20) . ' GMT');
        header("Cache-Pragma: public");
        header("Cache-Control: cache, must-revalidate");
        if (isset($_SERVER['If-Modified-Since']) && (strtotime($_SERVER['If-Modified-Since']) == filemtime($file))) {
            header('HTTP/1.1 304 Not Modified');
            header('Last-Modified: ' . date('D, d M Y H:i:s', filemtime($file)) . ' GMT');//304
            exit;
        } else {
            header('Last-Modified: ' . date('D, d M Y H:i:s', filemtime($file)) . ' GMT', true, 200);
        }
        $etag = '"' . md5(date('D, d M Y H:i:s', filemtime($file))) . '"';
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
            header('HTTP/1.1 304 Not Modified');
            header('Etag: ' . $etag);
            exit;
        } else {
            header('Etag: ' . $etag);
        }
    }
    header("X-Powered-By: kodExplorer.");
    header("Content-Type: " . $mime);
    header("Accept-Ranges: bytes");
    $size = get_filesize($file);
    $length = $size;           // Content length
    $start = 0;               // Start byte
    $end = $size - 1;       // End byte
    if (isset($_SERVER['HTTP_RANGE']) || isset($_GET['start'])) {//分段请求；视频拖拽
//        $c_start = $start;
        $c_end = $end;
        if (isset($_GET['start'])) {
            $c_start = intval($_GET['start']);
        } else {//range
            list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
            if (strpos($range, ',') !== false) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $start-$end/$size");
                exit;
            }
            if ($range == '-') {
                $c_start = $size - substr($range, 1);
            } else {
                $range = explode('-', $range);
                $c_start = $range[0];
                $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
            }
            $c_end = ($c_end > $end) ? $end : $c_end;
            if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $start-$end/$size");
                exit;
            }
        }
        $start = $c_start;
        $end = $c_end;
        $length = $end - $start + 1;
        header('HTTP/1.1 206 Partial Content');
    }
    header("Content-Range: bytes $start-$end/$size");
    header("Content-Length: " . $length);

    //header("X-Sendfile: $file");exit;
    if (!$fp = @fopen($file, "rb")) {
        exit;
    }
    fseek($fp, $start);
    while (!feof($fp)) {
        set_time_limit(0);
        print(fread($fp, 1024 * 4)); //输出文件
        flush();
        ob_flush();
    }
    fclose($fp);
}

/**
 * 远程文件下载到服务器
 * 支持fopen的打开都可以；支持本地、url
 *
 * @param $from
 * @param $file_name
 * @return bool
 */
function file_download_this($from, $file_name)
{
    set_time_limit(0);
    if ($fp = @fopen($from, "rb")) {
        if (!$download_fp = @fopen($file_name, "wb")) {
            return false;
        }
        while (!feof($fp)) {
            if (!file_exists($file_name)) {//删除目标文件；则终止下载
                fclose($download_fp);
                return false;
            }
            fwrite($download_fp, fread($fp, 1024 * 8), 1024 * 8);
        }
        //下载完成，重命名临时文件到目标文件
        fclose($download_fp);
        fclose($fp);
        return true;
    } else {
        return false;
    }
}

/**
 * 获取文件(夹)权限 rwx_rwx_rwx
 * @param $file
 * @return string
 */
function get_mode($file)
{
    $Mode = fileperms($file);
    $theMode = ' ' . decoct($Mode);
    $theMode = substr($theMode, -4);
    $Owner = array();
    $Group = array();
    $World = array();
    if ($Mode & 0x1000) $Type = 'p'; // FIFO pipe
    elseif ($Mode & 0x2000) $Type = 'c'; // Character special
    elseif ($Mode & 0x4000) $Type = 'd'; // Directory
    elseif ($Mode & 0x6000) $Type = 'b'; // Block special
    elseif ($Mode & 0x8000) $Type = '-'; // Regular
    elseif ($Mode & 0xA000) $Type = 'l'; // Symbolic Link
    elseif ($Mode & 0xC000) $Type = 's'; // Socket
    else $Type = 'u'; // UNKNOWN
    // Determine les permissions par Groupe
    $Owner['r'] = ($Mode & 00400) ? 'r' : '-';
    $Owner['w'] = ($Mode & 00200) ? 'w' : '-';
    $Owner['x'] = ($Mode & 00100) ? 'x' : '-';
    $Group['r'] = ($Mode & 00040) ? 'r' : '-';
    $Group['w'] = ($Mode & 00020) ? 'w' : '-';
    $Group['e'] = ($Mode & 00010) ? 'x' : '-';
    $World['r'] = ($Mode & 00004) ? 'r' : '-';
    $World['w'] = ($Mode & 00002) ? 'w' : '-';
    $World['e'] = ($Mode & 00001) ? 'x' : '-';
    // Adjuste pour SUID, SGID et sticky bit
    if ($Mode & 0x800) $Owner['e'] = ($Owner['e'] == 'x') ? 's' : 'S';
    if ($Mode & 0x400) $Group['e'] = ($Group['e'] == 'x') ? 's' : 'S';
    if ($Mode & 0x200) $World['e'] = ($World['e'] == 'x') ? 't' : 'T';
    $Mode = $Type . $Owner['r'] . $Owner['w'] . $Owner['x'] . ' ' .
        $Group['r'] . $Group['w'] . $Group['e'] . ' ' .
        $World['r'] . $World['w'] . $World['e'];
    return $Mode . ' (' . $theMode . ') ';
}

/**
 * 获取可以上传的最大值
 * return * byte
 */
function get_post_max()
{
    $upload = ini_get('upload_max_filesize');
    $upload = $upload == '' ? ini_get('upload_max_size') : $upload;
    $post = ini_get('post_max_size');
    $upload = intval($upload) * 1024 * 1024;
    $post = intval($post) * 1024 * 1024;
    return $upload < $post ? $upload : $post;
}

/**
 * 文件上传处理。单个文件上传,多个分多次请求
 * 调用demo
 * upload('file','D:/www/');
 * @param $fileInput
 * @param string $path
 */
function upload($fileInput, $path = './')
{
    global $config, $L;
    $file = $_FILES[$fileInput];
    if (!isset($file)) show_json($L['upload_error_null'], false);

    $file_name = iconv_system($file['name']);
    $save_path = get_filename_auto($path . $file_name);
    if (move_uploaded_file($file['tmp_name'], $save_path)) {
        show_json($L['upload_success'], true, isset($save_pathe) ? iconv_app($save_pathe) : '');
    } else {
        show_json($L['move_error'], false);
    }
}

//分片上传处理
function upload_chunk($fileInput, $path = './', $temp_path)
{
    global $L;
    $file = $_FILES[$fileInput];
    $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
    $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
    if (!isset($file)) show_json($L['upload_error_null'], false);
    $file_name = iconv_system($file['name']);

    if ($chunks > 1) {//并发上传，不一定有前后顺序
        $temp_file_pre = $temp_path . md5($temp_path . $file_name) . '.part';
        if (get_filesize($file['tmp_name']) == 0) {
            show_json($L['upload_success'], false, 'chunk_' . $chunk . ' error!');
        }
        if (move_uploaded_file($file['tmp_name'], $temp_file_pre . $chunk)) {
            $done = true;
            for ($index = 0; $index < $chunks; $index++) {
                if (!file_exists($temp_file_pre . $index)) {
                    $done = false;
                    break;
                }
            }
            if (!$done) {
                show_json($L['upload_success'], true, 'chunk_' . $chunk . ' success!');
            }

            $save_path = $path . $file_name;
            $out = fopen($save_path, "wb");
            if ($done && flock($out, LOCK_EX)) {
                for ($index = 0; $index < $chunks; $index++) {
                    if (!$in = fopen($temp_file_pre . $index, "rb")) break;
                    while ($buff = fread($in, 4096)) {
                        fwrite($out, $buff);
                    }
                    fclose($in);
                    unlink($temp_file_pre . $index);
                }
                flock($out, LOCK_UN);
                fclose($out);
            }
            show_json($L['upload_success'], true, iconv_app($save_path));
        } else {
            show_json($L['move_error'], false);
        }
    }

    //正常上传
    $save_path = get_filename_auto($path . $file_name); //自动重命名
    if (move_uploaded_file($file['tmp_name'], $save_path)) {
        show_json($L['upload_success'], true, iconv_app($save_path));
    } else {
        show_json($L['move_error'], false);
    }
}

/**
 * 写日志
 * @param string $log 日志信息
 * @param string $type 日志类型 [system|app|...]
 * @param string $level 日志级别
 * @return boolean
 */
function write_log($log, $type = 'default', $level = 'log')
{
    $now_time = date('[y-m-d H:i:s]');
    $now_day = date('Y_m_d');
    // 根据类型设置日志目标位置
    $target = LOG_PATH . strtolower($type) . '/';
    mk_dir($target, 0777);
    if (!is_writable($target)) exit('path can not write!');
    switch ($level) {// 分级写日志
        case 'error':
            $target .= 'Error_' . $now_day . '.log';
            break;
        case 'warning':
            $target .= 'Warning_' . $now_day . '.log';
            break;
        case 'debug':
            $target .= 'Debug_' . $now_day . '.log';
            break;
        case 'info':
            $target .= 'Info_' . $now_day . '.log';
            break;
        case 'db':
            $target .= 'Db_' . $now_day . '.log';
            break;
        default:
            $target .= 'Log_' . $now_day . '.log';
            break;
    }
    //检测日志文件大小, 超过配置大小则重命名
    if (file_exists($target) && get_filesize($target) <= 100000) {
        $file_name = substr(basename($target), 0, strrpos(basename($target), '.log')) . '.log';
        rename($target, dirname($target) . '/' . $file_name);
    }
    clearstatcache();
    return error_log("$now_time $log\n", 3, $target);
}

abstract class Controller
{
    public $in;
    public $db;
    public $config;    // 全局配置
    public $tpl;    // 模板目录
    public $values;    // 模板变量
    public $L;

    /**
     * 构造函数
     */
    function __construct()
    {
        global $in, $config, $db, $L;

        $this->db = $db;
        $this->L = $L;
        $this->config = &$config;
        $this->in = &$in;
        $this->values['config'] = &$config;
        $this->values['in'] = &$in;
    }


    /**
     * 加载类库文件
     * @param string $class
     */
    public function loadClass($class)
    {
        if (1 === func_num_args()) {
            $this->$class = new $class;
        } else {
            $reflectionObj = new ReflectionClass($class);
            $args = func_get_args();
            array_shift($args);
            $this->$class = $reflectionObj->newInstanceArgs($args);
        }
        return $this->$class;
    }

    /**
     * 显示模板
     * @param $key
     * @param $value
     * @return void
     */
    protected function assign($key, $value)
    {
        $this->values[$key] = $value;
    }

    /**
     * 显示模板
     * @param
     */
    protected function display($tpl_file)
    {
        global $L, $LNG;
        extract($this->values);
        require($this->tpl . $tpl_file);
    }
}

/**
 * 加载类，从class目录；controller；model目录中寻找class
 * @param $className
 */
function _autoload($className)
{
    if (file_exists(CLASS_DIR . strtolower($className) . '.class.php')) {
        require_once(CLASS_DIR . strtolower($className) . '.class.php');
    } else if (file_exists(CONTROLLER_DIR . strtolower($className) . '.class.php')) {
        require_once(CONTROLLER_DIR . strtolower($className) . '.class.php');
    } else {
        // error code;
    }
}

/**
 * 生产controller对象
 * @param $controller_name
 * @return bool|object
 */
function init_controller($controller_name)
{
    if (!class_exists($controller_name)) {
        $model_file = CONTROLLER_DIR . $controller_name . '.class.php';
        if (!is_file($model_file)) {
            return false;
        }
        require_once($model_file);
    }
    $reflectionObj = new ReflectionClass($controller_name);
    $args = func_get_args();
    array_shift($args);
    return $reflectionObj->newInstanceArgs($args);
}

/**
 * 加载类
 * @param $class
 */
function load_class($class)
{
    $filename = CLASS_DIR . $class . '.class.php';
    if (file_exists($filename)) {
        require($filename);
    } else {
        pr($filename . ' is not exist', 1);
    }
}

/**
 * 文本字符串转换
 * @param $str
 * @return mixed
 */
function mystr($str)
{
    $from = array("\r\n", " ");
    $to = array("<br/>", "&nbsp");
    return str_replace($from, $to, $str);
}

// 清除多余空格和回车字符
function strip($str)
{
    return preg_replace('!\s+!', '', $str);
}

/**
 * 获取精确时间
 */
function mtime()
{
    $t = explode(' ', microtime());
    $time = $t[0] + $t[1];
    return $time;
}

/**
 * 过滤HTML
 * @param $HTML
 * @param bool $br
 * @return mixed|string
 */
function clear_html($HTML, $br = true)
{
    $HTML = htmlspecialchars(trim($HTML));
    $HTML = str_replace("\t", ' ', $HTML);
    if ($br) {
        return nl2br($HTML);
    } else {
        return str_replace("\n", '', $HTML);
    }
}

/**
 * 将obj深度转化成array
 *
 * @param  mixed $obj 要转换的数据 可能是数组 也可能是个对象 还可能是一般数据类型
 * @return mixed 一般数据类型
 */
function obj2array($obj)
{
    if (is_array($obj)) {
        foreach ($obj as &$value) {
            $value = obj2array($value);
        }
        return $obj;
    } elseif (is_object($obj)) {
        $obj = get_object_vars($obj);
        return obj2array($obj);
    } else {
        return $obj;
    }
}

/**
 * 计算时间差
 *
 * @param string $pretime
 * @return string
 */
function spend_time(&$pretime)
{
    $now = microtime(1);
    $spend = round($now - $pretime, 5);
    $pretime = $now;
    return $spend;
}

function check_code($code)
{
    $fontsize = 18;
    $len = strlen($code);
    $width = 70;
    $height = 27;
    $im = @imagecreatetruecolor($width, $height) or die("create image error!");
    $background_color = imagecolorallocate($im, 255, 255, 255);
    imagefill($im, 0, 0, $background_color);
    for ($i = 0; $i < 2000; $i++) {//获取随机淡色
        $line_color = imagecolorallocate($im, mt_rand(180, 255), mt_rand(160, 255), mt_rand(100, 255));
        imageline($im, mt_rand(0, $width), mt_rand(0, $height), //画直线
            mt_rand(0, $width), mt_rand(0, $height), $line_color);
        imagearc($im, mt_rand(0, $width), mt_rand(0, $height), //画弧线
            mt_rand(0, $width), mt_rand(0, $height), $height, $width, $line_color);
    }
    $border_color = imagecolorallocate($im, 160, 160, 160);
    imagerectangle($im, 0, 0, $width - 1, $height - 1, $border_color);//画矩形，边框颜色200,200,200

    for ($i = 0; $i < $len; $i++) {//写入随机字串
//        $current = $code[mt_rand(0, strlen($code) - 1)];
        $text_color = imagecolorallocate($im, mt_rand(30, 140), mt_rand(30, 140), mt_rand(30, 140));
        imagechar($im, 10, $i * $fontsize + 6, rand(1, $height / 3), $code[$i], $text_color);
    }
    if (function_exists("imagejpeg")) {
        header("Content-Type: image/jpeg");
        imagejpeg($im, null, 90);//图片质量
    } else if (function_exists("imagegif")) {
        header("Content-Type: image/gif");
        imagegif($im);
    } else if (function_exists("imagepng")) {
        header("Content-Type: image/x-png");
        imagepng($im);
    }
    imagedestroy($im);//销毁图片
}

/**
 * 返回当前浮点式的时间,单位秒;主要用在调试程序程序时间时用
 *
 * @return float
 */
function microtime_float()
{
    list($usec, $sec) = explode(' ', microtime());
    return ((float)$usec + (float)$sec);
}

/**
 * 计算N次方根
 * @param $num
 * @param int $root
 * @return float
 */
function croot($num, $root = 3)
{
    $root = intval($root);
    if (!$root) {
        return $num;
    }
    return exp(log($num) / $root);
}

function add_magic_quotes($array)
{
    foreach ((array)$array as $k => $v) {
        if (is_array($v)) {
            $array[$k] = add_magic_quotes($v);
        } else {
            $array[$k] = addslashes($v);
        }
    }
    return $array;
}

// 字符串加转义
function add_slashes($string)
{
    if (!$GLOBALS['magic_quotes_gpc']) {
        if (is_array($string)) {
            foreach ($string as $key => $val) {
                $string[$key] = add_slashes($val);
            }
        } else {
            $string = addslashes($string);
        }
    }
    return $string;
}

/**
 * hex to binary
 */
if (!function_exists('hex2bin')) {
    function hex2bin($hexdata)
    {
        return pack('H*', $hexdata);
    }
}

/**
 * 二维数组按照指定的键值进行排序，
 *
 * @param mixed $arr
 * @param mixed $keys 根据键值
 * @param string $type 升序降序
 * @return array $array = array(
 * array('name'=>'手机','brand'=>'诺基亚','price'=>1050),
 * array('name'=>'手表','brand'=>'卡西欧','price'=>960)
 * );$out = array_sort($array,'price');
 */
function array_sort($arr, $keys, $type = 'asc')
{
    $keysvalue = $new_array = array();
    foreach ($arr as $k => $v) {
        $keysvalue[$k] = $v[$keys];
    }
    if ($type == 'asc') {
        asort($keysvalue);
    } else {
        arsort($keysvalue);
    }
    reset($keysvalue);
    foreach ($keysvalue as $k => $v) {
        $new_array[$k] = $arr[$k];
    }
    return $new_array;
}

/**
 * 遍历数组，对每个元素调用 $callback，假如返回值不为假值，则直接返回该返回值；
 * 假如每次 $callback 都返回假值，最终返回 false
 *
 * @param  $array
 * @param  $callback
 * @return mixed
 */
function array_try($array, $callback)
{
    if (!$array || !$callback) {
        return false;
    }
    $args = func_get_args();
    array_shift($args);
    array_shift($args);
    if (!$args) {
        $args = array();
    }
    foreach ($array as $v) {
        $params = $args;
        array_unshift($params, $v);
        $x = call_user_func_array($callback, $params);
        if ($x) {
            return $x;
        }
    }
    return false;
}

// 求多个数组的并集
function array_union()
{
    $argsCount = func_num_args();
    if ($argsCount < 2) {
        return false;
    } else if (2 === $argsCount) {
        list($arr1, $arr2) = func_get_args();

        while ((list(, $v) = each($arr2))) {
            if (!in_array($v, $arr1)) $arr1[] = $v;
        }
        return $arr1;
    } else { // 三个以上的数组合并
        $arg_list = func_get_args();
        $all = call_user_func_array('array_union', $arg_list);
        return array_union($arg_list[0], $all);
    }
}

// 取出数组中第n项
function array_get($arr, $index)
{
    foreach ($arr as $k => $v) {
        $index--;
        if ($index < 0) return array($k, $v);
    }
    return null;
}

function show_tips($message)
{
    echo <<<END
<html>
	<style>
	#msgbox{border: 1px solid #ddd;border: 1px solid #eee;padding: 30px;border-radius: 5px;background: #f6f6f6;
	font-family: 'Helvetica Neue', "Microsoft Yahei", "微软雅黑", "STXihei", "WenQuanYi Micro Hei", sans-serif;
	color:888;font-size:13px;margin:0 auto;margin-top:10%;width: 400px;font-size: 16px;color:#666;}
	#msgbox #title{padding-left:20px;font-weight:800;font-size:25px;}
	#msgbox #message{padding:20px;}
	</style>
	<body>
	<div id="msgbox">
	<div id="title">tips</div>
	<div id="message">$message</div>
	</body>
</html>
END;
    exit;
}

/**
 * 打包返回AJAX请求的数据
 * @params {int} 返回状态码， 通常0表示正常
 * @params {array} 返回的数据集合
 * @param $data
 * @param bool $code
 * @param string $info
 * @return void
 */
function show_json($data, $code = true, $info = '')
{
    $use_time = mtime() - $GLOBALS['config']['app_startTime']??0;
    $result = array('code' => $code, 'use_time' => $use_time, 'data' => $data);
    $info and $result['info'] = $info;
    header("X-Powered-By: naz.");
    header('Content-Type: application/json; charset=utf-8');
    exit(json_encode($result));
}

/**
 * 简单模版转换，用于根据配置获取列表：
 * 参数：cute1:第一次切割的字符串，cute2第二次切割的字符串,
 * arraylist为待处理的字符串，$this 为标记当前项，$this_str为当项标记的替换。
 * $tpl为处理后填充到静态模版({0}代表切割后左值,{1}代表切割后右值,{this}代表当前项填充值)
 * 例子：
 * $arr="default=淡蓝(默认)=5|mac=mac海洋=6|mac1=mac1海洋=7";
 * $tpl="<li class='list {this}' theme='{0}'>{1}_{2}</li>\n";
 * echo getTplList('|','=',$arr,$tpl,'mac'),'<br/>';
 * @param $cute1
 * @param $cute2
 * @param $arraylist
 * @param $tpl
 * @param $that
 * @param string $this_str
 * @return string
 */
function getTplList($cute1, $cute2, $arraylist, $tpl, $that, $this_str = '')
{
    $list = explode($cute1, $arraylist);
    if ($this_str == '') $this_str = "this";
    $html = '';
    foreach ($list as $value) {
        $info = explode($cute2, $value);
        $arr_replace = array();
        foreach ($info as $key => $val) {
            $arr_replace[$key] = '{' . $key . '}';
        }
        if ($info[0] == $that) {
            $temp = str_replace($arr_replace, $info, $tpl);
            $temp = str_replace('{this}', $this_str, $temp);
        } else {
            $temp = str_replace($arr_replace, $info, $tpl);
            $temp = str_replace('{this}', '', $temp);
        }
        $html .= $temp;
    }
    return $html;
}

//获取当前url地址
function get_url()
{
    $sys_protocal = isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT']
    == '443' ? 'https://' : 'http://';
    $php_self = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
    $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
    $relate_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] :
        $php_self . (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : $path_info);
    return $sys_protocal . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $relate_url;
}

/**
 * 去掉HTML代码中的HTML标签，返回纯文本
 * @param string $document 待处理的字符串
 * @return string
 */
function html2txt($document)
{
    $search = array("'<script[^>]*?>.*?</script>'si", // 去掉 javascript
        "'<[\/\!]*?[^<>]*?>'si", // 去掉 HTML 标记
        "'([\r\n])[\s]+'", // 去掉空白字符
        "'&(quot|#34);'i", // 替换 HTML 实体
        "'&(amp|#38);'i",
        "'&(lt|#60);'i",
        "'&(gt|#62);'i",
        "'&(nbsp|#160);'i",
        "'&(iexcl|#161);'i",
        "'&(cent|#162);'i",
        "'&(pound|#163);'i",
        "'&(copy|#169);'i",
        "'&#(\d+);'e"); // 作为 PHP 代码运行
    $replace = array("",
        "",
        "",
        "\"",
        "&",
        "<",
        ">",
        " ",
        chr(161),
        chr(162),
        chr(163),
        chr(169),
        "chr(\\1)");
    $text = preg_replace($search, $replace, $document);
    return $text;
}

// 获取内容第一条
function match($content, $preg)
{
    $preg = "/" . $preg . "/isU";
    preg_match($preg, $content, $result);
    return $result[1];
}

// 获取内容,获取一个页面若干信息.结果在 1,2,3……中
function match_all($content, $preg)
{
    $preg = "/" . $preg . "/isU";
    preg_match_all($preg, $content, $result);
    return $result;
}

/**
 * 获取指定长度的 utf8 字符串
 *
 * @param string $string
 * @param int $length
 * @param string $dot
 * @return string
 */
function get_utf8_str($string, $length, $dot = '...')
{
    if (strlen($string) <= $length) return $string;

    $n = $tn = $noc = 0;

    while ($n < strlen($string)) {
        $t = ord($string[$n]);
        if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
            $tn = 1;
            $n++;
            $noc++;
        } elseif (194 <= $t && $t <= 223) {
            $tn = 2;
            $n += 2;
            $noc += 2;
        } elseif (224 <= $t && $t <= 239) {
            $tn = 3;
            $n += 3;
            $noc += 2;
        } elseif (240 <= $t && $t <= 247) {
            $tn = 4;
            $n += 4;
            $noc += 2;
        } elseif (248 <= $t && $t <= 251) {
            $tn = 5;
            $n += 5;
            $noc += 2;
        } elseif ($t == 252 || $t == 253) {
            $tn = 6;
            $n += 6;
            $noc += 2;
        } else {
            $n++;
        }
        if ($noc >= $length) break;
    }
    if ($noc > $length) {
        $n -= $tn;
    }
    if ($n < strlen($string)) {
        $strcut = substr($string, 0, $n);
        return $strcut . $dot;
    } else {
        return $string;
    }
}

/**
 * 字符串截取，支持中文和其他编码
 *
 * @param string $str 需要转换的字符串
 * @param int $start 开始位置
 * @param string $length 截取长度
 * @param string $charset 编码格式
 * @param bool $suffix 截断显示字符
 * @return string
 */
function msubstr($str, $start = 0, $length, $charset = "utf-8", $suffix = true)
{
    if (function_exists("mb_substr")) {
        $i_str_len = mb_strlen($str);
        $s_sub_str = mb_substr($str, $start, $length, $charset);
        if ($length >= $i_str_len) {
            return $s_sub_str;
        }
        return $s_sub_str . '...';
    } elseif (function_exists('iconv_substr')) {
        return iconv_substr($str, $start, $length, $charset);
    }
    $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
    $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
    $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
    $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
    preg_match_all($re[$charset], $str, $match);
    $slice = join("", array_slice($match[0], $start, $length));
    if ($suffix) return $slice . "…";
    return $slice;
}

function web2wap(&$content, $publish_url = '')
{
    $search = array("/<img[^>]+src=\"([^\">]+)\"[^>]+>/siU",
        "/<a[^>]+href=\"([^\">]+)\"[^>]*>(.*)<\/a>/siU",
        "'<br[^>]*>'si",
        "'<p>'si",
        "'</p>'si",
        "'<script[^>]*?>.*?</script>'si", // 去掉 javascript
        "'<[\/\!]*?[^<>]*?>'si", // 去掉 HTML 标记
        "'([\r\n])[\s]+'", // 去掉空白字符
    ); // 作为 PHP 代码运行
    $replace = array("#img#\\1#/img#",
        "#link#\\1#\\2#/link#",
        "[br]",
        "",
        "[br]",
        "",
        "",
        "",
    );
    $text = preg_replace($search, $replace, $content);
    $text = str_replace("[br]", "<br/>", $text);
    $img_start = "<img src=\"" . $publish_url . "automini.php?src=";
    $img_end = "&amp;pixel=100*80&amp;cache=1&amp;cacheTime=1000&amp;miniType=png\" />";
    $text = preg_replace("/#img#(.*)#\/img#/isUe", "'$img_start'.urlencode('\\1').'$img_end'", $text);
    $text = preg_replace("/#link#(.*)#(.*)#\/link#/isU", "<a href=\"\\1\">\\2</a>", $text);
    while (preg_match("/<br\/><br\/>/siU", $text)) {
        $text = str_replace('<br/><br/>', '<br/>', $text);
    }
    return $text;
}

/**
 * 获取变量的名字
 * eg hello="123" 获取ss字符串
 * @param $aVar
 * @return int|string
 */
function get_var_name(&$aVar)
{
    foreach ($GLOBALS as $key => $var) {
        if ($aVar == $GLOBALS[$key] && $key != "argc") {
            return $key;
        }
    }
    return null;
}

// -----------------变量调试-------------------
/**
 * 格式化输出变量，或者对象
 *
 * @param mixed $var
 * @param boolean $exit
 */
function pr($var, $exit = false)
{
    ob_start();
    $style = '<style>
	pre#debug{margin:10px;font-size:14px;color:#222;line-height:1.2em;background:#f6f6f6;border-left:5px solid #444;padding:5px;width:95%;word-break:break-all;}
	pre#debug b{font-weight:400;}
	#debug #debug_str{color:#E75B22;}
	#debug #debug_keywords{font-weight:800;color:#00f;}
	#debug #debug_tag1{color:#22f;}
	#debug #debug_tag2{color:#f33;font-weight:800;}
	#debug #debug_var{color:#33f;}
	#debug #debug_var_str{color:#f00;}
	#debug #debug_set{color:#0C9CAE;}</style>';
    if (is_array($var)) {
        print_r($var);
    } else if (is_object($var)) {
        echo get_class($var) . " Object";
    } else if (is_resource($var)) {
        echo (string)$var;
    } else {
        echo var_export($var, true);
    }
    $out = ob_get_clean(); //缓冲输出给$out 变量
    $out = preg_replace('/"(.*)"/', '<b id="debug_var_str">"' . '\\1' . '"</b>', $out); //高亮字符串变量
    $out = preg_replace('/=\>(.*)/', '=>' . '<b id="debug_str">' . '\\1' . '</b>', $out); //高亮=>后面的值
    $out = preg_replace('/\[(.*)\]/', '<b id="debug_tag1">[</b><b id="debug_var">' . '\\1' . '</b><b id="debug_tag1">]</b>', $out); //高亮变量
    $from = array('    ', '(', ')', '=>');
    $to = array('  ', '<b id="debug_tag2">(</i>', '<b id="debug_tag2">)</b>', '<b id="debug_set">=></b>');
    $out = str_replace($from, $to, $out);

    $keywords = array('Array', 'int', 'string', 'class', 'object', 'null'); //关键字高亮
    $keywords_to = $keywords;
    foreach ($keywords as $key => $val) {
        $keywords_to[$key] = '<b id="debug_keywords">' . $val . '</b>';
    }
    $out = str_replace($keywords, $keywords_to, $out);
    $out = str_replace("\n\n", "\n", $out);
    echo $style . '<pre id="debug"><b id="debug_keywords">' . get_var_name($var) . '</b> = ' . $out . '</pre>';
    if ($exit) exit; //为真则退出
}

/**
 * 调试输出变量，对象的值。
 * 参数任意个(任意类型的变量)
 *
 * @return void
 */
function debug_out()
{
    $avg_num = func_num_args();
    $avg_list = func_get_args();
    ob_start();
    for ($i = 0; $i < $avg_num; $i++) {
        pr($avg_list[$i]);
    }
    $out = ob_get_clean();
    echo $out;
    exit;
}

/**
 * 取$from~$to范围内的随机数
 *
 * @param  int $from 下限
 * @param  int $to 上限
 * @return mixed
 */
function rand_from_to($from, $to)
{
    $size = $from - $to; //数值区间
    $max = 30000; //最大
    if ($size < $max) {
        return $from + mt_rand(0, $size);
    } else {
        if ($size % $max) {
            return $from + rand_from_to(0, $size / $max) * $max + mt_rand(0, $size % $max);
        } else {
            return $from + rand_from_to(0, $size / $max) * $max + mt_rand(0, $max);
        }
    }
}

/**
 * 产生随机字串，可用来自动生成密码 默认长度6位 字母和数字混合
 *
 * @param int $len 长度
 * @param string $type 字串类型：0 字母 1 数字 2 大写字母 3 小写字母  4 中文
 * 其他为数字字母混合(去掉了 容易混淆的字符oOLl和数字01，)
 * @return string
 */
function rand_string($len = 4, $type = 'check_code')
{
    $str = '';
    switch ($type) {
        case 0://大小写中英文
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
            break;
        case 1://数字
            $chars = str_repeat('0123456789', 3);
            break;
        case 2://大写字母
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            break;
        case 3://小写字母
            $chars = 'abcdefghijklmnopqrstuvwxyz';
            break;
        default:
            // 默认去掉了容易混淆的字符oOLl和数字01，要添加请使用addChars参数
            $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789';
            break;
    }
    if ($len > 10) { // 位数过长重复字符串一定次数
        $chars = $type == 1 ? str_repeat($chars, $len) : str_repeat($chars, 5);
    }
    if ($type != 4) {
        $chars = str_shuffle($chars);
        $str = substr($chars, 0, $len);
    } else {
        // 中文随机字
        for ($i = 0; $i < $len; $i++) {
            $str .= msubstr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1);
        }
    }
    return $str;
}

/**
 * 生成自动密码
 */
function make_password()
{
    $temp = '0123456789abcdefghijklmnopqrstuvwxyz' .
        'ABCDEFGHIJKMNPQRSTUVWXYZ~!@#$^*)_+}{}[]|":;,.' . time();
    for ($i = 0; $i < 10; $i++) {
        $temp = str_shuffle($temp . substr($temp, -5));
    }
    return md5($temp);
}


/**
 * php DES解密函数
 *
 * @param string $key 密钥
 * @param string $encrypted 加密字符串
 * @return string
 */
function des_decode($key, $encrypted)
{
    $encrypted = base64_decode($encrypted);
    $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_CBC, ''); //使用MCRYPT_DES算法,cbc模式
    $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    $ks = mcrypt_enc_get_key_size($td);

    mcrypt_generic_init($td, $key, $key); //初始处理
    $decrypted = mdecrypt_generic($td, $encrypted); //解密

    mcrypt_generic_deinit($td); //结束
    mcrypt_module_close($td);
    return pkcs5_unpad($decrypted);
}

/**
 * php DES加密函数
 *
 * @param string $key 密钥
 * @param string $text 字符串
 * @return string
 */
function des_encode($key, $text)
{
    $y = pkcs5_pad($text);
    $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_CBC, ''); //使用MCRYPT_DES算法,cbc模式
    $ks = mcrypt_enc_get_key_size($td);

    mcrypt_generic_init($td, $key, $key); //初始处理
    $encrypted = mcrypt_generic($td, $y); //解密
    mcrypt_generic_deinit($td); //结束
    mcrypt_module_close($td);
    return base64_encode($encrypted);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text) - 1});
    if ($pad > strlen($text)) return $text;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return $text;
    return substr($text, 0, -1 * $pad);
}

function pkcs5_pad($text, $block = 8)
{
    $pad = $block - (strlen($text) % $block);
    return $text . str_repeat(chr($pad), $pad);
}


//--------------------------------------- SETTING BEGIN -------------------------------------------------------------------
//配置数据,可在setting_user.php中更改覆盖
$config = [];
$config['settings'] = array(
    'download_url_time' => 0,            //下载地址生效时间，按秒计算，0代表不限制，默认不限制
    'upload_chunk_size' => 1024 * 1024 * 2,    //上传分片大小；默认1M
    'version_desc' => 'product',
);

//初始化系统配置
$config['setting_system_default'] = array(
    'system_password' => rand_string(10),
    'system_name' => "KodExplorer",
    'system_desc' => "——芒果云.资源管理器",
    'path_hidden' => ".DS_Store,.gitignore",//目录列表隐藏的项
    'auto_login' => "0",            // 是否自动登录；登录用户为guest
    'first_in' => "explorer",    // 登录后默认进入[explorer desktop,editor]
    'new_user_app' => "365日历,pptv直播,ps,qq音乐,搜狐影视,时钟,天气,水果忍者,计算器,豆瓣电台,音悦台,icloud",
    'new_user_folder' => "download,music,image,desktop",
);

// 配置项可选值
$config['setting_all'] = array(
    'language' => "en-US:English,zh_CN:简体中文,zh_TW:繁體中文",
    'themeall' => "default/:<b>areo blue</b>:default,simple/:<b>simple</b>:simple,metro/:<b>metro</b>:metro,metro/blue_:metro-blue:color,metro/leaf_:metro-green:color,metro/green_:metro-green+:color,metro/grey_:metro-grey:color,metro/purple_:metro-purple:color,metro/pink_:metro-pink:color,metro/orange_:metro-orange:color",
    'codethemeall' => "chrome,clouds,crimson_editor,eclipse,github,solarized_light,tomorrow,xcode,ambiance,idle_fingers,monokai,pastel_on_dark,solarized_dark,tomorrow_night_blue,tomorrow_night_eighties",
    'wallall' => "1,2,3,4,5,6,7,8,9,10,11,12,13",
    'musicthemeall' => "ting,beveled,kuwo,manila,mp3player,qqmusic,somusic,xdj",
    'moviethemeall' => "webplayer,qqplayer,vplayer,tvlive,youtube"
);

//新用户初始化配置
$config['setting_default'] = array(
    'list_type' => "icon",        // list||icon
    'list_sort_field' => "name",        // name||size||ext||mtime
    'list_sort_order' => "up",        // asc||desc
    'theme' => "simple/",    // app theme [default,simple,metro/,metro/black....]
    'codetheme' => "clouds",    // code editor theme
    'wall' => "7",            // wall picture
    'musictheme' => "mp3player",    // music player theme
    'movietheme' => "webplayer"    // movie player theme
);

//初始化默认菜单配置
$config['setting_menu_default'] = array(
    array('name' => 'desktop', 'type' => 'system', 'url' => 'explorer.php?desktop', 'target' => '_self', 'use' => '1'),
    array('name' => 'explorer', 'type' => 'system', 'url' => 'explorer.php?explorer', 'target' => '_self', 'use' => '1'),
    array('name' => 'editor', 'type' => 'system', 'url' => 'explorer.php?editor', 'target' => '_self', 'use' => '1'),
    array('name' => 'adminer', 'type' => '', 'url' => 'adminer.php', 'target' => '_blank', 'use' => '1')
);

//权限配置；精确到需要做权限控制的控制器和方法
//需要权限认证的Action;root组无视权限
$config['role_setting'] = array();
//--------------------------------------- SETTING END -------------------------------------------------------------------

//数据地址定义。
$config['pic_thumb'] = WS_PATH_DATA . '/thumb/';        // 缩略图生成存放地址
$config['cache_dir'] = WS_PATH_DATA . '/cache/';        // 缓存文件地址
$config['app_startTime'] = mtime();                    //起始时间

//系统编码配置
$config['app_charset'] = 'utf-8';            //该程序整体统一编码
$config['check_charset'] = 'ASCII,UTF-8,GBK';//文件打开自动检测编码
//when edit a file ;check charset and auto converto utf-8;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
    $config['system_os'] = 'windows';
    $config['system_charset'] = 'gbk';//user set your server system charset
} else {
    $config['system_os'] = 'linux';
    $config['system_charset'] = 'utf-8';
}
//------------------------------------- PARSE_INCOMING BEGIN ------------------------------------------------------------------------------
/**
 * GET/POST数据统一入口
 * 将GET和POST的数据进行过滤，去掉非法字符以及hacker code，返回一个数组
 * 注意如果GET和POST有相同的Key，POST优先
 *
 * @return array $_GET和$_POST数据过滤处理后的值
 */

$_COOKIE = Util::stripslashesDeepin($_COOKIE);
$_GET = (array)Util::stripslashesDeepin($_GET);
$_POST = Util::stripslashesDeepin($_POST);
$in = array();
$in = array_merge($_GET, $_POST);
$remote = array_get($in, 0);
$remote = explode('/', trim($remote[0], '/'));
$in['URLremote'] = $remote;
//------------------------------------- PARSE_INCOMING END ------------------------------------------------------------------------------

isset($in['PHPSESSID']) and session_id($in['PHPSESSID']);//office edit post

$config['autorun'] = array(
    array('controller' => 'user', 'function' => 'loginCheck'),
    array('controller' => 'user', 'function' => 'authCheck')
);

//--------------------------------------- INTI LANG ------------------------------------------------------------------
//语言包加载：优先级：cookie获取>自动识别
//首次没有cookie则自动识别——存入cookie,过期时间无限
global $L;
if (isset($_COOKIE['kod_user_language'])) {
    $lang = $_COOKIE['kod_user_language'];
} else {//没有cookie
    $lang = Request::getIP();
    setcookie('kod_user_language', $lang, time() + 3600 * 24 * 365);
}
define('LANGUAGE_TYPE', WS_LANG_EN);
$GLOBALS['L'] = Lang::getInstance(WS_LANG_EN)->getAll();
//--------------------------------------- INTI SETTING ------------------------------------------------------------------
global $config;
$setting_file = USER_SYSTEM . 'system_setting.php';
if (!file_exists($setting_file)) {//不存在则建立
    $setting = $config['setting_system_default'];
    $setting['menu'] = $config['setting_menu_default'];
    FileCache::save($setting_file, $setting);
} else {
    $setting = FileCache::load($setting_file);
}
if (!is_array($setting)) {
    $setting = $config['setting_system_default'];
}
if (!is_array($setting['menu'])) {
    $setting['menu'] = $config['setting_menu_default'];
}

$config['setting_system'] = $setting;//全局
$L['kod_name'] = $setting['system_name'];
$L['kod_name_desc'] = $setting['system_desc'];
if (isset($setting['powerby'])) {
    $L['kod_power_by'] = $setting['powerby'];
}

//加载用户自定义配置
is_file($setting_user = WS_PATH_CONFIG . 'setting_user.php') and require($setting_user);

global $in;

$URI = $in['URLremote'];

empty($URI[0]) and $URI[0] = $setting['first_in'];//设置默认控制器
empty($URI[1]) and $URI[1] = 'index';//设置默认控制器操作
define('ST', $URI[0]);
define('ACT', $URI[1]);

//登录过的前提下实现自动登录
$member = Member::getInstance();
$user = $member->get('admin');
//if ($user['status'] == 0) {//初始化app
//    $app = init_controller('app');
//    $app->init_app($user);
//}
$_SESSION['kod_login'] = true;
$_SESSION['kod_user'] = $user;
setcookie('kod_name', $user['name'], time() + 3600 * 24 * 365);
//setcookie('kod_token', md5($user['password'] . \Sharin\Library\UserAgent::getIp()), time() + 3600 * 24 * 365);

function instanceMethodCall(string $clsnm, string $method)
{
    $class = '\\application\\module\\explorer\\controller\\' . $clsnm;
    $instance = new $class();
    return $instance->$method();
}

if (count($config['autorun']) > 0) {
    foreach ($config['autorun'] as $key => $var) {
        instanceMethodCall($var['controller'], $var['function']);
    }
}
instanceMethodCall(ST, ACT);
